package com.diamond.webservice.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.diamond.webservice.entity.JsEcAsyn;
import com.diamond.webservice.entity.JsEcInfo;
import com.diamond.webservice.entity.Page;
import com.diamond.webservice.mapper.JsEcInfoMapper;
import com.diamond.webservice.service.JsEcAsynService;
import com.diamond.webservice.service.RequestMessageService;
import com.diamond.webservice.service.TransmissionService;
import com.diamond.webservice.util.HttpUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@Service
public class TransmissionServiceImpl implements TransmissionService {
    @Autowired
    private JsEcInfoMapper jsEcInfoMapper;

    @Autowired
    private RequestMessageService requestMessageService;

    @Autowired
    private JsEcAsynService jsEcAsynService;

    private static final Logger LOG = LoggerFactory.getLogger(TransmissionService.class);

    public void transmitManyMain(int transmitCapacity) {
        // 1、查询待传输的条数
        int allNotTransmitCount = jsEcInfoMapper.queryWaitTransmitCount();
        LOG.error("待传数量：" + allNotTransmitCount);
        // 2、这里限定一次传输任务传输最大可传transmitCapacity条数据,需要执行任务的次数taskCount=allNotTransmitCount/100 + 1
        int taskCount;  // taskCount为需要发送报文请求的次数
        if (allNotTransmitCount % transmitCapacity == 0) {
            taskCount = allNotTransmitCount / transmitCapacity;
        } else {
            taskCount = allNotTransmitCount / transmitCapacity + 1;
        }

        // 方式一：循环传输
        for(int i = 0;i<taskCount;i++){
            List<JsEcInfo> jsEcInfos = jsEcInfoMapper.queryWaitTransmit(new Page(1, transmitCapacity));
            transmitData(jsEcInfos);
        }
        // 方式二：多线程传输
//        //核心线程数
//        int corePoolSize = 4;
//        //最大线程数
//        int maximumPoolSize = 10;
//        //超过 corePoolSize 线程数量的线程最大空闲时间
//        long keepAliveTime = 2;
//        //以秒为时间单位
//        TimeUnit unit = TimeUnit.SECONDS;
//        //创建工作队列，用于存放提交的等待执行任务
//        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<Runnable>(taskCount);
//        ThreadPoolExecutor threadPoolExecutor = null;
//        try {
//            //创建线程池
//            threadPoolExecutor = new ThreadPoolExecutor(corePoolSize,
//                    maximumPoolSize,
//                    keepAliveTime,
//                    unit,
//                    workQueue,
//                    new ThreadPoolExecutor.AbortPolicy());
//
//             获取所有要传输的数据
//             List<JsEcInfo> jsEcInfos = jsEcInfoMapper.queryWaitTransmit(new Page(1, allNotTransmitCount));
//            //循环提交任务,
//            for (int i = 0; i < taskCount; i++) {
//                final List<JsEcInfo> jsEcInfosWait = jsEcInfos.subList(i*transmitCapacity,transmitCapacity*(i+1));
//                threadPoolExecutor.submit(new Runnable() {
//                    public void run() {
//                        transmitData(jsEcInfosWait);
//                    }
//                });
//                //每个任务提交后休眠500ms再提交下一个任务，用于保证提交顺序
//                Thread.sleep(500);
//            }
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        } finally {
//            threadPoolExecutor.shutdown();
//        }

    }

    public void transmitMain(int transmitCapacity) {
        // 1、查询待传输的条数
        int allNotTransmitCount = jsEcInfoMapper.queryWaitTransmitCount();
        LOG.error("待传数量：" + allNotTransmitCount);
        // 2、这里限定一次传输任务传输最大可传transmitCapacity条数据,需要执行任务的次数taskCount=allNotTransmitCount/100 + 1
        int taskCount;  // 任务数量
        if (allNotTransmitCount % transmitCapacity == 0) {
            taskCount = allNotTransmitCount / transmitCapacity;
        } else {
            taskCount = allNotTransmitCount / transmitCapacity + 1;
        }
        // 3、循环执行任务
        for (int i = 0; i < taskCount; i++) {
            doOneTask(transmitCapacity);
        }
    }

    /**
     * 一次传输任务
     * @param transmitCapacity 一次任务要传输的证书信息数量
     */
    public void doOneTask(int transmitCapacity) {
        //1、拉取100条待传输的数据
        List<JsEcInfo> jsEcInfos = jsEcInfoMapper.queryWaitTransmit(new Page(1, transmitCapacity));
        //2、这里按一批次100条数据计算，100条数据分五个线程去执行传输任务
        //核心线程数
        int corePoolSize = 8;
        //最大线程数
        int maximumPoolSize = 10;
        //超过 corePoolSize 线程数量的线程最大空闲时间
        long keepAliveTime = 2;
        //以秒为时间单位
        TimeUnit unit = TimeUnit.SECONDS;
        //创建工作队列，用于存放提交的等待执行任务
        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<Runnable>(transmitCapacity);
        ThreadPoolExecutor threadPoolExecutor = null;
        try {
            //创建线程池
            threadPoolExecutor = new ThreadPoolExecutor(corePoolSize,
                    maximumPoolSize,
                    keepAliveTime,
                    unit,
                    workQueue,
                    new ThreadPoolExecutor.AbortPolicy());

            //循环提交任务
            for (int i = 0; i < jsEcInfos.size(); i++) {
                //提交任务的索引
                final int index = (i);
                final JsEcInfo jsEcInfo = jsEcInfos.get(index);
                threadPoolExecutor.submit(new Runnable() {
                    public void run() {
                        transmitData(jsEcInfo);
                    }
                });
                //每个任务提交后休眠500ms再提交下一个任务，用于保证提交顺序
                Thread.sleep(500);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            threadPoolExecutor.shutdown();
        }
    }

    /**
     * 传输一条证书信息
     *
     * @param jsEcInfo 待传输的数据
     */
    public void transmitData(JsEcInfo jsEcInfo) {
        // 1、构建请求报文
        List list = new ArrayList<JsEcInfo>();
        list.add(jsEcInfo);
        String message = requestMessageService.initTransmissionParam(list);
        // 2、调用接口请求
        String result = "";
        try {
            result = HttpUtil.sendHttpPost("http://10.72.30.111:10011/dzzzinte/jtCommon/archive/system", message);
        } catch (Exception e) {
            LOG.error("START*****网络请求异常*****");
            LOG.error("异常详情：" + e.getMessage());
            LOG.error("END*****网络请求异常*****");
        }
        // 3、记录传输结果
        if (!StringUtils.isEmpty(result)) {
            JSONObject outJson = JSONObject.parseObject(result);
            JSONObject header = outJson.getJSONObject("header");
            String status = header.getString("status");
            String errorinfo = header.getString("errorinfo");
            // 将返回报文存储起来
            jsEcInfo.setIsTransmit(status);
            jsEcInfo.setErrorInfo(errorinfo);
            JsEcAsyn jsEcAsyn = new JsEcAsyn(jsEcInfo.getId(), "00".equals(status) ? 1 : 0);
            jsEcAsynService.saveTransmitResult(jsEcInfo,jsEcAsyn);
//            int i = jsEcInfoMapper.updateTransmit(jsEcInfo);
        }
    }

    /**
     * 传输若干条证书数据
     *
     * @param jsEcInfoList 待传输的证书信息集合
     */
    public void transmitData(List<JsEcInfo> jsEcInfoList) {
        //1、构建请求报文
        String message = requestMessageService.initTransmissionParam(jsEcInfoList);
        // 2、调用接口请求
        String result = "";
        try {
            result = HttpUtil.sendHttpPost("http://10.72.30.111:10011/dzzzinte/jtCommon/archive/system", message);
        } catch (Exception e) {
            LOG.error("START*****网络请求异常*****");
            LOG.error("异常详情：" + e.getMessage());
            LOG.error("END*****网络请求异常*****");
        }
        // 3、记录传输结果
        if (!StringUtils.isEmpty(result)) {
            JSONObject outJson = JSONObject.parseObject(result);
            JSONObject header = outJson.getJSONObject("header");
            String status = header.getString("status");
            String errorinfo = header.getString("errorinfo");
            // 解析errorinfo
            // 如果报文返回"00"，说明所有的全部传输成功，则将这批数据的is_transmit字段都置为"00"
            if("00".equals(status)){
                for (JsEcInfo jsEcInfo : jsEcInfoList) {
                    jsEcInfo.setIsTransmit("00");
                    jsEcAsynService.saveTransmitResult(jsEcInfo,new JsEcAsyn(jsEcInfo.getId(),1));
                }
            }else if("10".equals(status)){
            // 如果返回其他值，则说明部分传输成功，成功的置为"00"，其他的置为10
                Map<String, String> resultMap = parseErrorinfo(errorinfo);
                // 说明返回结果不可解析（一般是因为证照编号为空），则重新传输一遍这些
                if(resultMap == null){
                    for (JsEcInfo jsEcInfo : jsEcInfoList) {
                        transmitData(jsEcInfo);
                    }
                }else{
                    for (JsEcInfo jsEcInfo : jsEcInfoList) {
                        JsEcAsyn jsEcAsyn = new JsEcAsyn();
                        if(resultMap.keySet().contains(jsEcInfo.getCertificateNo())){
                            jsEcInfo.setIsTransmit("10");
                            jsEcInfo.setErrorInfo(resultMap.get(jsEcInfo.getCertificateNo()));
                            jsEcAsyn.setEcId(jsEcInfo.getId());
                            jsEcAsyn.setIsSuccess(0);
                        }else{
                            jsEcInfo.setIsTransmit("00");
                            jsEcAsyn.setEcId(jsEcInfo.getId());
                            jsEcAsyn.setIsSuccess(1);
                        }
                        jsEcAsynService.saveTransmitResult(jsEcInfo,jsEcAsyn);
                    }
                }
            }else {
                for (JsEcInfo jsEcInfo : jsEcInfoList) {
                    transmitData(jsEcInfo);
                }
            }
        }
    }

    /**
     * 解析错误结果
     * @param errorinfo
     * @return
     */
    public static Map<String,String> parseErrorinfo(String errorinfo){
        Map map = null;
        try{
             map = new HashMap<String,String>();
            String[] noAndInfoList = errorinfo.split("；");
            System.out.println(noAndInfoList.length);
            for (int i = 0;i<noAndInfoList.length;i++) {
                String[] noAndInfo = noAndInfoList[i].split("：");
                map.put(noAndInfo[0],noAndInfo[1]);
            }
        }catch (Exception e){
            return null;
        }
        return map;
    }
}
